package com.zzedu.manager.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zzedu.common.constant.Constants;
import com.zzedu.common.enums.CommonEnums;
import com.zzedu.common.enums.LoginType;
import com.zzedu.common.enums.PlatformLoginEnumns;
import com.zzedu.common.enums.UserTypeEnumns;
import com.zzedu.common.utils.MessageUtils;
import com.zzedu.common.utils.SecurityUtils;
import com.zzedu.common.utils.ServletUtils;
import com.zzedu.common.utils.StringUtils;
import com.zzedu.device.req.BindDevice;
import com.zzedu.device.req.LineDevice;
import com.zzedu.device.req.Login;
import com.zzedu.device.req.LoginDevice;
import com.zzedu.device.rsp.ApiLoginVO;
import com.zzedu.device.rsp.LoginDeviceVO;
import com.zzedu.domain.*;
import com.zzedu.enmuns.SchoolStateEnums;
import com.zzedu.framework.lock.IDistributedLockService;
import com.zzedu.framework.manager.factory.AsyncFactory;
import com.zzedu.framework.web.domain.LoginForm;
import com.zzedu.integration.lbs.IIpResolveServiceClient;
import com.zzedu.integration.lbs.dto.IpLocationDTO;
import com.zzedu.manager.IZzAppDeviceManager;
import com.zzedu.result.Result;
import com.zzedu.result.ResultEnum;
import com.zzedu.service.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.session.data.redis.RedisSessionRepository;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

@Service
public class ZzAppDeviceManagerImpl implements IZzAppDeviceManager {

    private static final Logger log = LoggerFactory.getLogger(ZzAppDeviceManagerImpl.class);
    @Autowired
    IZzAppDeviceBindCodeService zzAppDeviceBindCodeService;

    @Autowired
    IZzAppDeviceService zzAppDeviceService;

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    IZzAppDeviceLoginService zzAppDeviceLoginService;

    @Autowired
    IZzTeacherService zzTeacherService;

    @Autowired
    RedisSessionRepository redisIndexedSessionRepository;

    @Autowired
    IZzSchoolService zzSchoolService;

    @Autowired
    IDistributedLockService distributedLockService;

    @Autowired
    IIpResolveServiceClient iIpResolveServiceClient;

    @Override
    public Result bindDevice(BindDevice data) {
        String lockName = "bind:" + data.getSn() + ":" + data.getMac();
        return distributedLockService.executeWithLock(lockName, 10L, 30L, TimeUnit.SECONDS, () -> {
            Result result = new Result<>();
            result.setResultCode(ResultEnum.SUCCESS.getCode());
            result.setResultMessage(ResultEnum.SUCCESS.getDesc());
            LambdaQueryWrapper<ZzAppDeviceBindCode> lambdaQueryWrapper = Wrappers.lambdaQuery(ZzAppDeviceBindCode.class);
            lambdaQueryWrapper.eq(ZzAppDeviceBindCode::getBindCode, data.getCode());
            lambdaQueryWrapper.ge(ZzAppDeviceBindCode::getExpireAt, LocalDateTime.now());
            lambdaQueryWrapper.last("limit 1");
            ZzAppDeviceBindCode zzAppDeviceBindCode = zzAppDeviceBindCodeService.getOne(lambdaQueryWrapper);
            if (null == zzAppDeviceBindCode) {
                result.setResultCode(ResultEnum.FAIL.getCode());
                result.setResultMessage("验证码无效");
                result.setData("验证码无效");
                return result;
            }
            ZzSchool zzSchool = this.zzSchoolService.getBaseMapper().selectById(zzAppDeviceBindCode.getSchoolId());
            if (null == zzSchool) {
                result.setResultMessage("校区不存在");
                result.setResultCode(ResultEnum.FAIL.getCode());
                return result;
            }
            SchoolStateEnums schoolStateEnums = SchoolStateEnums.getCode(zzSchool.getState());
            if (SchoolStateEnums.DISABLE == schoolStateEnums) {
                result.setResultMessage("校区已停用");
                result.setResultCode(ResultEnum.FAIL.getCode());
                return result;
            }
            long count = zzAppDeviceService.count(Wrappers.lambdaQuery(ZzAppDevice.class).eq(ZzAppDevice::getSchoolId, zzSchool.getId()));
            if (count + 1 > zzSchool.getBindDeviceNum()) {
                result.setResultMessage("当前校区绑定设备已达上限");
                result.setResultCode(ResultEnum.FAIL.getCode());
                return result;
            }
            ZzAppDevice zzAppDevice = new ZzAppDevice();
            zzAppDevice.setDeviceSn(data.getSn());
            zzAppDevice.setDeviceMac(data.getMac());
            zzAppDevice.setBindIp(SecurityUtils.getIp());
            zzAppDevice.setBindDate(LocalDateTime.now());
            zzAppDevice.setBindId(zzAppDeviceBindCode.getId());
            zzAppDevice.setSchoolId(zzAppDeviceBindCode.getSchoolId());
            zzAppDevice.setState(CommonEnums.YES.getCode());
            zzAppDeviceService.save(zzAppDevice);
            result.setData(zzAppDevice.getId());

            CompletableFuture.runAsync(() -> {
                try {

                    IpLocationDTO location = iIpResolveServiceClient.getRealAddressByIP(zzAppDevice.getBindIp());
                    if (null != location) {
                        if ("1".equals(location.getStatus())) {
                            String bindLocation = String.join(",", "省份: " + location.getProvince(), "城市: " + location.getCity(), "区县: " + location.getDistrict(), "经纬度: (" + location.getLongitude() + ", " + location.getLatitude() + ")");
                            zzAppDevice.setBindLocation(bindLocation);
                            if (StringUtils.isNotBlank(bindLocation)) {
                                zzAppDeviceService.update(Wrappers.lambdaUpdate(ZzAppDevice.class)
                                        .eq(ZzAppDevice::getId, zzAppDevice.getId())
                                        .set(ZzAppDevice::getBindLocation, bindLocation)
                                );
                            }
                        }

                    }
                } catch (Exception e) {
                    log.error("解析ip位置服务异常,详细:" + e.getMessage());
                }
            });
            return result;
        });

    }

    @Override
    public Result<LoginDeviceVO> login(LoginDevice data) {
        Result<LoginDeviceVO> result = new Result<>();
        try {
            result.setResultCode(ResultEnum.SUCCESS.getCode());
            result.setResultMessage(ResultEnum.SUCCESS.getDesc());
            LambdaQueryWrapper<ZzAppDevice> lambdaQueryWrapper = Wrappers.lambdaQuery(ZzAppDevice.class);
            lambdaQueryWrapper.eq(ZzAppDevice::getDeviceSn, data.getSn());
            lambdaQueryWrapper.eq(ZzAppDevice::getDeviceMac, data.getMac());
            lambdaQueryWrapper.last("limit 1");
            ZzAppDevice zzAppDevice = zzAppDeviceService.getOne(lambdaQueryWrapper);
            LoginDeviceVO loginDeviceVO = new LoginDeviceVO();
            loginDeviceVO.setNeedBind(CommonEnums.NO);
            if (null == zzAppDevice) {
                loginDeviceVO.setNeedBind(CommonEnums.YES);
                result.setData(loginDeviceVO);
                return result;
            }
            ZzTeacher zzTeacher = zzTeacherService.getOne(Wrappers.lambdaQuery(ZzTeacher.class).eq(ZzTeacher::getTel, data.getLoginName()).eq(ZzTeacher::getState, CommonEnums.YES.getCode()).last("limit 1"));
            if (null == zzTeacher) {
                result.setResultMessage("账号不存在或已停用");
                result.setResultCode(ResultEnum.FAIL.getCode());
                return result;
            }
            if (!zzAppDevice.getSchoolId().equals(zzTeacher.getSchoolId())) {
                result.setResultMessage("当前老师账号和学校终端不匹配");
                result.setResultCode(ResultEnum.FAIL.getCode());
                return result;
            }
            ZzSchool zzSchool = this.zzSchoolService.getById(zzTeacher.getSchoolId());
            if (null == zzSchool) {
                result.setResultMessage("校区不存在");
                result.setResultCode(ResultEnum.FAIL.getCode());
                return result;
            }
            SchoolStateEnums schoolStateEnums = SchoolStateEnums.getCode(zzSchool.getState());
            if (SchoolStateEnums.DISABLE == schoolStateEnums) {
                result.setResultMessage("校区已停用");
                result.setResultCode(ResultEnum.FAIL.getCode());
                return result;
            }
            if (StringUtils.isNotBlank(zzTeacher.getLoginToken())) {
                try {
                    //强制下线
                    redisIndexedSessionRepository.deleteById(zzTeacher.getLoginToken());
                } catch (Exception e) {
                    log.error("强制下线失败,详细:{}", e.getMessage());
                }
            }
            LoginForm loginForm = new LoginForm();
            loginForm.setLoginType(LoginType.PWD);
            loginForm.setPlatformLoginEnumns(PlatformLoginEnumns.PC);
            loginForm.setPassword(data.getPwd());
            loginForm.setUsername(data.getLoginName());
            loginForm.setUserTypeEnumns(UserTypeEnumns.DEVICE_TEACHER);
            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(JSON.toJSONString(loginForm), data.getPwd());
            Authentication authentication = authenticationManager.authenticate(token);
            SecurityContext securityContext = SecurityContextHolder.getContext();
            securityContext.setAuthentication(authentication);
            ServletUtils.getRequest().getSession(true).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, securityContext);
            AsyncFactory.recordLogininfor(SecurityUtils.getLoginName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
            result.setResultMessage(ResultEnum.SUCCESS.getDesc());
            result.setResultCode(ResultEnum.SUCCESS.getCode());
            loginDeviceVO.setToken(ServletUtils.getSession().getId());
            result.setData(loginDeviceVO);
            //写入记录
            ZzAppDeviceLogin zzAppDeviceLogin = new ZzAppDeviceLogin();
            zzAppDeviceLogin.setDeviceId(zzAppDevice.getId());
            zzAppDeviceLogin.setIp(SecurityUtils.getIp());
            zzAppDeviceLoginService.save(zzAppDeviceLogin);
            zzTeacher.setLoginToken(loginDeviceVO.getToken());
            zzTeacherService.updateById(zzTeacher);
            String ip =SecurityUtils.getIp();
            CompletableFuture.runAsync(() -> {
                try {
                    IpLocationDTO location = iIpResolveServiceClient.getRealAddressByIP(ip);
                    if (null != location) {
                        if ("1".equals(location.getStatus())) {
                            String bindLocation = String.join(",", "省份: " + location.getProvince(), "城市: " + location.getCity(), "区县: " + location.getDistrict(), "经纬度: (" + location.getLongitude() + ", " + location.getLatitude() + ")");
                            if (StringUtils.isNotBlank(bindLocation)) {
                                zzAppDeviceLoginService.update(Wrappers.lambdaUpdate(ZzAppDeviceLogin.class)
                                        .eq(ZzAppDeviceLogin::getId, zzAppDeviceLogin.getId())
                                        .set(ZzAppDeviceLogin::getLocation, bindLocation)
                                );
                            }
                        }
                    }
                } catch (Exception e) {
                    log.error("解析ip位置服务异常,详细:" + e.getMessage());
                }
            });

            zzAppDevice.setLastLoginDate(LocalDateTime.now());
            zzAppDevice.setLastLoginIp(SecurityUtils.getIp());
            zzAppDevice.setLastLoginUserId(zzTeacher.getId());
            zzAppDeviceService.updateById(zzAppDevice);
            return result;
        } catch (Exception e) {
            String msg = "用户或密码错误";
            if (StringUtils.isNotEmpty(e.getMessage())) {
                msg = e.getMessage();
            }
            result.setResultMessage(msg);
            result.setResultCode(ResultEnum.FAIL.getCode());
            return result;
        }
    }

    @Override
    public Result lineTime(LineDevice data) {
        ZzTeacher zzTeacher = zzTeacherService.getById(SecurityUtils.getUserId());
        if (null != zzTeacher) {
            LambdaQueryWrapper<ZzAppDevice> lambdaQueryWrapper = Wrappers.lambdaQuery(ZzAppDevice.class);
            lambdaQueryWrapper.eq(ZzAppDevice::getDeviceSn, data.getSn());
            lambdaQueryWrapper.eq(ZzAppDevice::getSchoolId, zzTeacher.getSchoolId());
            lambdaQueryWrapper.eq(ZzAppDevice::getDeviceMac, data.getMac());
            lambdaQueryWrapper.last("limit 1");
            ZzAppDevice zzAppDevice = zzAppDeviceService.getOne(lambdaQueryWrapper);
            if (null != zzAppDevice) {
                Long lineTime = 0L;
                if (null == zzAppDevice.getPreLineTime()) {
                    lineTime = 0L;
                } else {
                    lineTime = zzAppDevice.getLineTime();
                    lineTime += ChronoUnit.MINUTES.between(zzAppDevice.getPreLineTime(), LocalDateTime.now());
                }
                LambdaUpdateWrapper<ZzAppDevice> lambdaUpdateWrapper = Wrappers.lambdaUpdate(ZzAppDevice.class);
                lambdaUpdateWrapper.set(ZzAppDevice::getPreLineTime, LocalDateTime.now());
                lambdaUpdateWrapper.set(ZzAppDevice::getLineTime, lineTime);
                lambdaUpdateWrapper.set(ZzAppDevice::getLastLoginIp, SecurityUtils.getIp());
                lambdaUpdateWrapper.eq(ZzAppDevice::getId, zzAppDevice.getId());
                zzAppDeviceService.update(lambdaUpdateWrapper);
                String ip = SecurityUtils.getIp();
                CompletableFuture.runAsync(() -> {
                    try {
                        IpLocationDTO location = iIpResolveServiceClient.getRealAddressByIP(ip);
                        if (null != location) {
                            if ("1".equals(location.getStatus())) {
                                String bindLocation = String.join(",", "省份: " + location.getProvince(), "城市: " + location.getCity(), "区县: " + location.getDistrict(), "经纬度: (" + location.getLongitude() + ", " + location.getLatitude() + ")");
                                if (StringUtils.isNotBlank(bindLocation)) {
                                    zzAppDeviceService.update(Wrappers.lambdaUpdate(ZzAppDevice.class)
                                            .eq(ZzAppDevice::getId, zzAppDevice.getId())
                                            .set(ZzAppDevice::getLastOperationLocation, bindLocation)
                                    );
                                }
                            }
                        }
                    } catch (Exception e) {
                        log.error("解析ip位置服务异常,详细:" + e.getMessage());
                    }
                });
            }
        }
        Result result = new Result<>();
        result.setResultMessage(ResultEnum.SUCCESS.getDesc());
        result.setResultCode(ResultEnum.SUCCESS.getCode());
        return result;
    }

    @Override
    public Result<ApiLoginVO> wxMiniLogin(Login data) {
        Result<ApiLoginVO> result = new Result<>();
        try {
            result.setResultCode(ResultEnum.SUCCESS.getCode());
            result.setResultMessage(ResultEnum.SUCCESS.getDesc());
            LoginForm loginForm = new LoginForm();
            LoginType loginType = data.getPwdType();
            if (null == loginType) {
                loginType = LoginType.PWD;
            }
            loginForm.setLoginType(loginType);
            loginForm.setPlatformLoginEnumns(PlatformLoginEnumns.PC);
            loginForm.setPassword(data.getPassWord());
            loginForm.setUsername(data.getUserName());
            loginForm.setUserTypeEnumns(UserTypeEnumns.DEVICE_TEACHER);

            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(JSON.toJSONString(loginForm), data.getPassWord());
            Authentication authentication = authenticationManager.authenticate(token);
            SecurityContext securityContext = SecurityContextHolder.getContext();
            securityContext.setAuthentication(authentication);
            ServletUtils.getRequest().getSession(true).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, securityContext);
            AsyncFactory.recordLogininfor(SecurityUtils.getLoginName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
            ApiLoginVO apiLoginVO = new ApiLoginVO();
            result.setResultMessage(ResultEnum.SUCCESS.getDesc());
            result.setResultCode(ResultEnum.SUCCESS.getCode());
            apiLoginVO.setToken(ServletUtils.getSession().getId());
            result.setData(apiLoginVO);
            return result;
        } catch (Exception e) {
            String msg = "用户或密码错误";
            if (StringUtils.isNotEmpty(e.getMessage())) {
                msg = e.getMessage();
            }
            result.setResultMessage(msg);
            result.setResultCode(ResultEnum.FAIL.getCode());
            return result;
        }
    }
}
